# --------------------------------------------------------------------------------------------------------------------
# Project:             Digital Payment System (DPS)
# Website:             http://code.google.com/p/dps-x509/
# Purpose of document: This is the main entry point into the exchanger web site
# --------------------------------------------------------------------------------------------------------------------
from google.appengine.ext import db, webapp
from google.appengine.ext.webapp import util
from time import strftime

import urllib
import logging
import base64
import binascii
import sys
import string

sys.path.append("..")

#you will probably need these
#load our vendor packages
sys.path.insert(0, "simplejson")
sys.path.insert(1, "pyasn1")
sys.path.insert(2, "keyczar")
sys.path.insert(3, "dps")
sys.path.insert(4, "test")
#sys.path.insert(0, "simplejson.zip")
#sys.path.insert(1, "pyasn1.zip")
#sys.path.insert(2, "keyczar.zip")
sys.path.insert(0, "simplejson")
sys.path.insert(1, "pyasn1")
sys.path.insert(2, "keyczar")

try:
  import simplejson as json
except ImportError:
  import json

#import dps.utils
#import dps.errors
#import dps.x509Certificate
#import dps.x509Helper
#import dps.x509CertificateParams  
  
from Certificate import Certificate  
from Rate import Rate
  
# --------------------------------------------------------------------------------------------------------------------
def standardHeader():
  return "<html><h1>The Exchanger</h1>" +standardMenu()+"<hr />"
  
# --------------------------------------------------------------------------------------------------------------------
def standardMenu():
  return """<a href="/exchanger/list">Search</a> / 
	        <a href="/exchanger/converter">Convert</a> /
		    <a href="/exchanger/exchange">Exchange</a> /
		    <a href="/exchanger/upload">Upload</a> /
		    <a href="/exchanger/manage">Manage</a> /
		    <a href="http://code.google.com/p/dps-x509/wiki/TheDIYElectronicCurrenciesExchanger">Documentation</a>"""

  
# --------------------------------------------------------------------------------------------------------------------
class DpsHandler(webapp.RequestHandler):
  """ Base class for web requests """
  
  def get(self):
    self.error(404)
    
  def post(self):
    self.error(404)


# --------------------------------------------------------------------------------------------------------------------
class MainHandler(DpsHandler):
  """ Passes all 'stray' messages onto the home page """
  
  def get(self):
    self.redirect("/exchanger/")


# --------------------------------------------------------------------------------------------------------------------
class ExchangerHandler(DpsHandler):
  """ Home page """
  
  def get(self):
	self.response.out.write(standardHeader() +"""<h3>Hei, welcome to the exchanger!</h3>
								<ul>
									<li><a href="/exchanger/list">Search token</a></li>
									<li><a href="/exchanger/converter">Exchange Rates</a></li>
									<li><a href="/exchanger/exchange">Exchange token</a></li>
									<li><a href="/exchanger/upload">Upload token (admin)</a></li>
									<li><a href="/exchanger/manage">Manage rates (admin)</a></li>
									<li><a href="http://code.google.com/p/dps-x509/wiki/TheDIYElectronicCurrenciesExchanger">Documentation</a></li>
								</ul>
								""")
    
    
# --------------------------------------------------------------------------------------------------------------------
class UploadHandler(DpsHandler):
  """ Upload Token (admin) """

# --------------------------------------------------------------------------------------------------------------------  
  def get(self):
    self.response.out.write(standardHeader() +"""<h2>Certificate upload</h2>
                               <form method='post' action='/registrar/upload' enctype='multipart/form-data'>
                                 <input name='certificate' type='file'/><br/>
                                 <input type='submit' value='Submit'/><br/>
                                 <input name='format' type='hidden' value='html'/>
                               </form>""")
							 
# --------------------------------------------------------------------------------------------------------------------
  def __getPotentialPublicKeys(self, cert):
    """ find the list of potential matches for public key in the store or existing cert 
    @param certificate in the form of dps.x509Certificate.X509Certificate
    @returns map of keys in the form db key-> public key 
    where public key is in the form of keyczar.keys.RsaPublicKey or None if not found """
    dps.utils.assertion(isinstance(cert, dps.x509Certificate.X509Certificate), \
                        "UploadHandler.__getPotentialPublicKeys: Incorrect cert type")   
    pkmap = {}
    certs = Certificate.all()
    issuerBase64 = cert.getIssuerAsBase64()
    for c in certs:  
      if issuerBase64 == c.subjectBase64_:
        pkmap[c.key()] = c.params().getPublicKey()
    return pkmap
      
# --------------------------------------------------------------------------------------------------------------------
  def __isCertificateInRegistrar(self, cert):
    """ returns true if the certificate is found in the registrar
    @param certificate in the form of dps.x509Certificate.X509Certificate 
    @returns true if found in db """
    dps.utils.assertion(isinstance(cert, dps.x509Certificate.X509Certificate), \
                        "UploadHandler.__isCertificateInRegistrar: Incorrect cert type")
    certs = Certificate.all()
    base64 = cert.asBase64()
    for c in certs:  
      if c.base64RawCertificate_ == base64:
        return True
    return False
        #raise dps.errors.CertificateAlreadyInRegistrarError()    
    

# --------------------------------------------------------------------------------------------------------------------
  def post(self):
    """ see http://code.google.com/p/dps-x509/wiki/RegistrarPrototypeDescription for the complete list of return codes """
    result = ""
    try:
      logging.info(self.request.get("certificate"))
      certData = urllib.unquote(self.request.get("certificate"))
      
      tempCertChain = dps.x509Helper.certsToBase64(certData)
      if len(tempCertChain) == 0:
        dps.utils.out("UploadCertificateHandler.post: No data in file")
        raise dps.errors.DecodingError()
      
      # certs are in order from self signed to most recently allocated
      certChain = []
      for c in tempCertChain:
        certChain.append(dps.x509Certificate.X509Certificate.load(c))
         
      for i in range(len(certChain)-2,-1,-1):        
        certChain[i].setIssuerCertificate(certChain[i+1]) 
      baseCert = certChain[:1][0]
      leafCert = certChain[-1:][0]
      
      currentPositionInChain = 0
      for i in range(len(certChain)-1,-1,-1):
        currentCert = certChain[i]
        dps.utils.out(str(len(certChain))+" "+str(i))
        
        publicKey = currentCert.getIssuerPublicKey()
        if publicKey and not currentCert.verifySignature(publicKey):
          raise dps.errors.InvalidSignatureError()
        
        posInChain = None
        if not publicKey:
          pkmap = self.__getPotentialPublicKeys(currentCert)
        
          matchKey = None
          for k, v in pkmap:
            if currentCert.verifySignature(v):
              matchKey = k
              break
            
          if not matchKey:
            raise dps.errors.IssuerPublicKeyNotFoundError()
        
          matchedCert = db.get(matchKey)
          if matchedCert.isActive_: #it is no longer active
            matchedCert.isActive_ = False
            matchedCert.put()
            posInChain = matchedCert.positionInChain_ + 1
          else:
            raise dps.errors.DoubleSpendError()
        
        #if self.__isCertificateInRegistrar(currentCert):
        #  #currentPositionInChain = currentCert.position
        #  if currentCert == leafCert:
        #    raise dps.errors.CertificateAlreadyInRegistrarError()
        #  else:
        #    #todo: check for double spend here...
        #    pass
        #else:
        #  c = Certificate()
          
          params = currentCert.getParams()
          dps.utils.out(params)
          c.base64RawCertificate_ = currentCert.asBase64()
          c.issuer_               = str(params.getIssuer())
          c.subject_              = str(params.getSubject())
          c.subjectBase64_        = currentCert.getSubjectAsBase64()
          c.redeemer_             = params.redeemer_
          c.tokenType_            = params.tokenType_      
          #python is not supporting float for 2.5 python i have so store cents value of token
          c.tokenQuantity_        = int(100*params.tokenQuantity_) 
          c.positionInChain_      = posInChain or currentPositionInChain
          c.isActive_             = currentCert == leafCert
          
          c.put()
        currentPositionInChain += 1
      
      result = "Valid"
      
    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in UploadCertificateHandler.post: " + str(sys.exc_info()[0]))
      result = "Rejected:"+e.what()
    
    format = self.request.get("format")
    if format == "html":
      self.response.out.write(standardHeader()+"<h2>Certificate Upload</h2><p>"+result+"</p>"+standardFooter())
    else: #assume json for now
      ret = json.dumps({"returnCode":result})
      dps.utils.out("ret: "+ret)
      self.response.out.write(ret)
							   

# --------------------------------------------------------------------------------------------------------------------
class ConverterHandler(DpsHandler):
  """ Token Converter """
  
  def get(self):
    self.response.out.write(standardHeader() +"""<h2>Token converter</h2>
                               <form method='post' action='/registrar/converter'>
                                 <input name='certificateFrom' type='text'/>
								 <select name='tokenTypeFrom'>
									<option>Token Type</option>
								 </select>
								 >> <input name='certificateTo' type='text'/>
								 <select name='tokenTypeTo'>
									<option>Token Type</option>
								 </select>
                                 <input type='submit' value='Convert'/><br/>
                                 <input name='format' type='hidden' value='html'/>
                               </form>""")							   


# --------------------------------------------------------------------------------------------------------------------
class SearchHandler(DpsHandler):
  """ Search Token """					   
  def get(self):
    self.response.out.write(standardHeader() +"""<h2>Search token</h2>
                               <form method='post' action='/registrar/search'>
								 <input name='keyword' type='text'/>
                                 <input type='submit' value='Search'/><br/>
                                 <input name='format' type='hidden' value='html'/>
                               </form>""")								   
							   
# --------------------------------------------------------------------------------------------------------------------
class ManageRatesHandler(DpsHandler):
  """ Manage Rates (admin) """
		
   
# --------------------------------------------------------------------------------------------------------------------
def main():
  #dps.utils.setLogMode(dps.utils.LogOutputModes.LOG_ONLY)

  
  application = webapp.WSGIApplication([
										("/exchanger/manage",  ManageRatesHandler),
                                        ("/exchanger/upload",  UploadHandler),
										("/exchanger/list",  SearchHandler),
                                        ("/exchanger/converter",  ConverterHandler),
										("/exchanger/exchange",  MainHandler), #undone
										("/exchanger/.*",  ExchangerHandler),
										("/.*",  MainHandler)],
                                       debug=True)
  util.run_wsgi_app(application)

if __name__ == "__main__":
  main()
